Schema (effect)
型
Decoding
外部からの入力(JSONなど)を TypeScript の型に変換
Encoding
TypeScript の値を外部形式(例:文字列)に変換
Asserting
値が指定されたスキーマに準拠しているかチェック
Schema生成
JSON Schema を自動で生成
Pretty Printing
データの見やすい表示
Arbitrary生成
code:_
┌─────────┐ ┌───┐ ┌───┐ ┌─────────┐
| unknown | | A | | I | | unknown |
└─────────┘ └───┘ └───┘ └─────────┘
| | | |
| validate | | |
|─────────────►│ | |
| | | |
| is | | |
|─────────────►│ | |
| | | |
| asserts | | |
|─────────────►│ | |
| | | |
| encodeUnknown| | |
|─────────────────────────►| |
| | |
| encode | |
|──────────►│ |
| | |
| decode | |
| ◄─────────| |
| | |
| | decodeUnknown|
| ◄────────────────────────|
decode
失敗したらthrow error
code:ts
Schema.decodeUnknownSync(Person)(input)
カリー化されてるんやmrsekut.icon
Option型を返す
Either型を返す
decodeしてPromiseを返す
なぜ?mrsekut.icon
code:ts
const asyncSchema = Schema.transformOrFail(PersonId, Person, {
strict: true,
// Decode with simulated async transformation
decode: (id) =>
Effect.succeed({ id, name: "name", age: 18 }).pipe(
Effect.delay("10 millis")
),
encode: (person) =>
Effect.succeed(person.id).pipe(Effect.delay("10 millis"))
})
Effect.runPromise(Schema.decodeUnknown(asyncSchema)(1)).then(console.log)
非同期な変換が含まれるSchemaというのがあるらしい
decodeしてEffectを返す
exact
デフォルト
code:ts
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.Unknown })
const input = {}
console.log(Schema.decodeUnknownSync(schema)(input))
// Output: { a: undefined }
exact: true
code:ts
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.Unknown })
const input = {}
console.log(Schema.decodeUnknownSync(schema)(input, { exact: true }))
/*
throws
ParseError: { readonly a: unknown }
└─ is missing
*/
まあ、decode時のみの挙動なら許容かmrsekut.icon
encoding
失敗したらthrow error
Option型を返す
Either型を返す
encodeしてPromise返す
encodeしてEffect返す
eoncdeがサポートされていないschemaもある
値にあって、Schemaにないpropertyは削除される
onExcessPropertyを使えば残すこともできる
encode時のエラーの取得
propertyの順序を固定できる
いつ使う年mrsekut.icon
code:ts
const mySymbol = Symbol.for("mySymbol")
const schema = Schema.UniqueSymbolFromSelf(mySymbol)
.pick
.omit
.partial
.required
変換処理などを取り除いて、SchemaのTypeだけ取り出す
encode形式だけ取り出す
変換前の制約だけ残す
任意の型(この例では File)に対してスキーマを定義できる
code:ts
const FileFromSelf = Schema.declare(
(input: unknown): input is File => input instanceof File
)
declare の第一引数は 型ガード関数。
decodeUnknownSync を使うことで、そのスキーマに対して バリデーション + デコード ができます。
型コンストラクタ
genericなSchemaも定義できる
QuickCheck風のランダムデータ生成(Arbitrary)には、追加のアノテーションが必要です:
code:ts
arbitrary: () => (fc) =>
これにより FastCheck.sample を使ってテスト用のデータを生成できます。
「外部のデータ構造」から「内部の型」への変換ルールを表現するもの
code:ts
import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.propertySignature(Schema.NumberFromString).annotations({
title: "Age" // Annotation to label the age field
})
})
これは "age" フィールドが string(例: "18")として与えられても、内部的には number として扱えるようにするスキーマを定義しています。
🔍 PropertySignature の型パラメータ
code:_
PropertySignature<ToToken, ToType, FromKey, FromToken, FromType, HasDefault, Context>
それぞれの意味は:
table:_
パラメータ 意味
ToToken 内部型(出力)での : or ?:(必須かオプショナルか)
ToType 出力時の型(例: number)
FromKey 入力側のフィールド名。省略時はToと同じ
FromToken 入力側の : or ?:(必須かオプショナルか)
FromType 入力時の型(例: string)
HasDefault デフォルト値があるか(true or false)
Context 注釈やメタ情報など(通常 never)
例:
code:ts
age: PropertySignature<":", number, never, ":", string, false, never>
これは次のように読み取れます:
age フィールドは必須(:)
出力は number
入力も必須(:)
入力は string
デフォルト値なし
🔁 フィールド名のマッピング(fromKey)
外部データのキー名が異なる場合には、Schema.fromKey を使います。
code:ts
const Person = Schema.Struct({
age: Schema.propertySignature(Schema.NumberFromString).pipe(
Schema.fromKey("AGE")
)
})
この例では "AGE" というキーを内部の age にマッピングしています。
1. なぜ変換(Transform)が必要か
スキーマは「型」と「エンコード形式(保存や通信での表現)」の両方を扱います。
Decode: 外部データ(文字列やJSON)をアプリ内部の型に変換
Encode: 内部の型を外部形式に戻す
このとき、スキーマ同士をつなぐ仕組みが Schema.transform / Schema.transformOrFail です。
特徴
失敗する可能性のある変換で使う
decode / encode 関数は ParseResult を返す必要がある
非同期変換も可能(Effect を返せる)
例(文字列→数値)
code:ts
export const NumberFromString = Schema.transformOrFail(
Schema.String,
Schema.Number,
{
strict: true,
decode: (s, _, ast) => {
const n = parseFloat(s)
return isNaN(n)
? ParseResult.fail(new ParseResult.Type(ast, s, "Not a number"))
: ParseResult.succeed(n)
},
encode: (n) => ParseResult.succeed(n.toString())
}
)
"123" → 123
"-" → 失敗(ParseError を返す)
4. strict オプション
true: 型の一致を厳しくチェック(型エラーが出やすい)
false: 型の柔軟性を許容(キャストや範囲外の値を許す)
例えば「数値を範囲にクランプする」ケース:
code:ts
decode: (a) => Number.clamp(a, { minimum, maximum }) as A
とキャストで無理やり A にするか、
code:ts
strict: false
で緩めるかを選べる。
6. 非同期・依存注入
外部APIで検証する場合 → transformOrFail + Effect
サービス依存(DI)が必要なら Context.Tag を使って依存関係を注入可能
7. 一方向変換(例: パスワードハッシュ)
encode 側で必ず失敗を返すようにして「戻せない変換」を表現できる
例: 平文パスワード → ハッシュ化
ハッシュ → 平文は Forbidden エラー
schema
Introduction
Getting Started
Basic Usage
Filters
Advanced Usage
Projections
Transformations
Annotations
説明つけるやつ
Error Messages
Error Formatters
エラーを見やすっく
Class APIs
Default Constructors
Effect Data Types
クソ長い
Overview
Interop With Data
Config
Option
OptionFromSelf
OptionFromUndefinedOr
OptionFromNullOr
OptionFromNullishOr
OptionFromNonEmptyTrimmedString
Exit
Handling Defects in Serialization
ExitFromSelf
ReadonlySet
ReadonlySet
ReadonlySetFromSelf
HashSet
HashSetFromSelf
SortedSet
SortedSet
SortedSetFromSelf
Duration
Duration
DurationFromSelf
DurationFromMillis
DurationFromNanos
clampDuration
Redacted
Redacted
RedactedFromSelf
Standard Schema
JSON Schema
Equivalence
Pretty Printer